package client

import (
	"context"
	"encoding/json"
	"gitee.com/zxs-micro/zxs-micro-common/configs"
	"gitee.com/zxs-micro/zxs-micro-common/impls"
	"gitee.com/zxs-micro/zxs-micro-common/log"
	commonModel "gitee.com/zxs-micro/zxs-micro-common/model"
	commonUtils "gitee.com/zxs-micro/zxs-micro-common/utils"
	"gitee.com/zxs-micro/zxs-micro-loadbalance/client/utils"
	"gitee.com/zxs-micro/zxs-micro-loadbalance/model"
	"gitee.com/zxs-micro/zxs-micro-proto/protos/common"
	"google.golang.org/grpc"
	"google.golang.org/grpc/connectivity"
	"sync"
	"time"
)

type LoadClient struct{
	lc common.ServerLoadClient
	load model.LoadModel
	impls.CommonClients
}

var cs map[string]*LoadClient
var tids map[string][]string
var clock sync.Mutex
var choosers map[string]utils.Chooser

func init() {
	cs = make(map[string]*LoadClient)
	tids = make(map[string][]string)
	choosers = make(map[string]utils.Chooser)
	commonUtils.AddThirdRandomFunc(randomServerWithLoadBalance)
	go timerCheckServiceLoad()
}

func randomServerWithLoadBalance(types string,m map[string]commonModel.Fuwu) (*commonModel.Fuwu, error) {
	co,ok := choosers[types]
	if !ok {
		log.Log.Warningf("类型[%s]的选择器不存在！取消本次使用负载均衡选择服务！",types)
		return nil,nil
	}
	s,ok := m[co.Pick()]
	if !ok {
		return nil,nil
	}
	log.Log.Debugf("类型[%s]的选择器已经选择出最优负载节点，ip为：%s",types,s.RemoteIp)
	return &s,nil
}

func GenerateLoadBalance(list *map[string]map[string]commonModel.Fuwu){
	for {
		//每10秒重新设置一次所有服务的负载选择器
		<- time.After(time.Second * 10)
		chos := make(map[string][]utils.Choice)
		tmpChooser := make(map[string]utils.Chooser)
		for k,v := range *list{
			cho,ok := chos[k]
			if !ok {
				cho = make([]utils.Choice,0)
			}
			for id,fw := range v{
				go generateLoadClient(k,id,fw.GetFuwuAccessIp())
				weight := uint(1)
				existFw,ok := cs[id]
				if ok {
					weight = uint(1000 / existFw.load.LoadAvg)
				}
				cho = append(cho,utils.Choice{id,weight})
			}
			tmpChooser[k] = utils.NewChooser(cho...)
		}
		choosers = tmpChooser
	}
}

func generateLoadClient(types,selfId,addr string) {
	var err error
	clock.Lock()
	cli, ok := cs[selfId]
	clock.Unlock()
	if !ok {
		cli = new(LoadClient)
		cli.ClientConn, err = grpc.Dial(addr, configs.GetClientDialOpts()...)
		if err != nil {
			return
		}
		cli.lc = common.NewServerLoadClient(cli.ClientConn)
		cli.RemoteAddr = addr
		clock.Lock()
		cs[selfId] = cli
		var ids []string
		if ids,ok = tids[types];!ok {
			ids = make([]string,0)
		}
		ids = append(ids,selfId)
		tids[types] = ids
		clock.Unlock()
	}
	//return cli, nil
}

func (c *LoadClient) CheckConn() (err error) {
	c.ClientLock.Lock()
	if c.ClientConn.GetState() == connectivity.Shutdown {
		c.ClientConn, err = grpc.Dial(c.RemoteAddr, configs.GetClientDialOpts()...)
		c.lc = common.NewServerLoadClient(c.ClientConn)
	}
	c.ClientLock.Unlock()
	return
}

func (c *LoadClient) ShutdownClient() {
	c.ClientLock.Lock()
	c.ClientConn.Close()
	c.ClientLock.Unlock()
	clock.Lock()
	delete(cs, c.RemoteAddr)
	clock.Unlock()
}

func (m *LoadClient) GetServiceLoad() ([]byte, error) {
	err := m.CheckConn()
	if err != nil {
		return nil, err
	}
	//获取各个服务器的负载状态，来进行负载均衡操作
	resp, err := m.lc.Load(context.Background(), &common.ServerLoadReq{
		Current: true,
	})
	if err != nil {
		return nil, err
	}
	return resp.Loads, nil
}

func timerCheckServiceLoad(){
	for{
		<-time.After(time.Second * 5)
		var wg sync.WaitGroup
		for k,o := range cs{
			o.CheckConn()
			wg.Add(1)
			go func(id string,client *LoadClient) {
				defer wg.Done()
				resp,err := client.lc.Load(context.Background(), &common.ServerLoadReq{
					Current: true,
				})
				if err != nil {
					clock.Lock()
					delete(cs,id)
					clock.Unlock()
					return
				}
				var lm model.LoadModel
				err = json.Unmarshal(resp.Loads,&lm)
				if err == nil {
					client.load = lm
				}
			}(k,o)
		}
		wg.Wait()

	}

}
